#include "ExampleApplication.h"
#include "QuadGridMesh.h"

#include "QuadTree.h"
#include "QuadTreeNode.h"
#include "AxisAlignedBox.h"

using namespace Ogre;

class OgreQuadTree 
	: public QuadTreeLib::QuadTree
	, public Ogre::SceneManager::Listener
{
protected:

public:
	OgreQuadTree(size_t maxLevel) : QuadTree(maxLevel)
	{
	}

	void addNode(Ogre::SceneNode* parentNode, const Ogre::Vector3& scale, const Ogre::Vector3& pos)
	{
		SceneNode* ogreNode = parentNode->createChildSceneNode();
		ogreNode->setScale(scale);
		ogreNode->setPosition(pos);
	}

	// Ogre::Listener::
	void postFindVisibleObjects(SceneManager* scnMgr, SceneManager::IlluminationRenderStage irs, Viewport* v)
	{
		scnMgr->getRenderQueue()->addRenderable(, );
	}
};

class OgreQuadTreeNode : public QuadTreeLib::QuadTreeNode, public Ogre::SimpleRenderable
{
protected:
	Ogre::String mNodeName;
public:
	OgreQuadTreeNode(Ogre::SceneManager* scnMgr, Ogre::String& nodeName, Ogre::SceneNode* parentNode, size_t lodLevel, float size, const Ogre::AxisAlignedBox& ogreAABB)
		: QuadTreeNode(lodLevel, size)
		, mNodeName(nodeName)
	{
		mAABB.Min.x = ogreAABB.getMinimum().x;
		mAABB.Min.y = ogreAABB.getMinimum().y;
		mAABB.Min.z = ogreAABB.getMinimum().z;
		mAABB.Max.x = ogreAABB.getMaximum().x;
		mAABB.Max.y = ogreAABB.getMaximum().y;
		mAABB.Max.z = ogreAABB.getMaximum().z;
	}
};

#define MAX_NODE_LEVEL 3

OgreQuadTree g_QuadTree(MAX_NODE_LEVEL);
const float lodRanges[MAX_NODE_LEVEL] = {250, 500, 1000};

bool createTerrainTexture(const char* heightmapFilename, const char* texName)
{
#if 0
	Image heightmapSrc;
	heightmapSrc.load(heightmapFilename, "General");
	const uint8* pSrc = heightmapSrc.getData();
	const size_t width = heightmapSrc.getWidth();
	const size_t height = heightmapSrc.getHeight();
	uchar bpp = heightmapSrc.getBPP();
	LogManager::getSingleton().logMessage(String(heightmapFilename) + ": " + StringConverter::toString(width) + "x" + StringConverter::toString(height) + "x" + StringConverter::toString(bpp));
#else
	const size_t width = 64;
	const size_t height = 64;
#endif
	TexturePtr texture = TextureManager::getSingleton().createManual(
		texName,          // name
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
		TEX_TYPE_2D,      // type
		width, height,    // width & height
		0,                // number of mipmaps
		PF_FLOAT32_R,     // pixel format
		TU_DEFAULT);      // usage

	HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer();

	pixelBuffer->lock(HardwareBuffer::HBL_NORMAL);
	const PixelBox& pixelBox = pixelBuffer->getCurrentLock();
	float* pDest = static_cast<float*>(pixelBox.data);

	for (size_t j = 0; j < height; j++)
		for(size_t i = 0; i < width; i++)
		{
#if 0
			*pDest++ = *pSrc++ / 255.0f;
#else
			*pDest++ = 0;
#endif
		}

	// Unlock the pixel buffer
	pixelBuffer->unlock();

	MaterialPtr material = MaterialManager::getSingleton().getByName("TerrainGridMaterial");
	material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(texName);
	//material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(TFO_NONE);
	//material->load();

	return true;
}

class TestListener : public ExampleFrameListener
{
protected:
	bool processUnbufferedKeyInput(const FrameEvent& evt)
	{
		if (!ExampleFrameListener::processUnbufferedKeyInput(evt))
			return false;
#ifdef MORPHLERP_BY_KEY
		if (mKeyboard->isKeyDown(OIS::KC_ADD) && mTimeUntilNextToggle <= 0)
		{
			if (i < 10)
				lerpK = ++i/10.0f;
			terrainGridParams->setNamedConstant("g_morphLerpK", lerpK);
			mTimeUntilNextToggle = 0.2f;
		}
		else if (mKeyboard->isKeyDown(OIS::KC_MINUS) && mTimeUntilNextToggle <= 0)
		{
			if (i > 0)
				lerpK = (float)--i/10.0f;
			terrainGridParams->setNamedConstant("g_morphLerpK", lerpK);
			mTimeUntilNextToggle = 0.2f;
		}
#endif
		return true;
	}

	bool frameStarted(const FrameEvent& evt)
	{
		return true;
	}

	GpuProgramParametersSharedPtr terrainGridParams;
	float lerpK;
	int i;

public:
    TestListener(RenderWindow* win, Camera* cam)
        : ExampleFrameListener(win, cam)
    {
		MaterialPtr material = MaterialManager::getSingleton().getByName("TerrainGridMaterialRed");
		terrainGridParams = material->getTechnique(0)->getPass(0)->getVertexProgramParameters();
		lerpK = 0; i = 0;
	}
};

class TestApplication : public ExampleApplication
{
public:
	TestApplication(LPSTR strCmdLine)
	{
	}
protected:

	void createFrameListener(void)
    {
		// This is where we instantiate our own frame listener
        mFrameListener = new TestListener(mWindow, mCamera);
        mRoot->addFrameListener(mFrameListener);
    }

	void createScene()
	{
		const unsigned int gridDim = 64;

		createTerrainTexture("black.png", "HeightmapTexture");

		QuadGridMesh* mMesh = new QuadGridMesh(64);
/*
		MaterialPtr gridMat = MaterialManager::getSingleton().getByName("TerrainGridMaterial");
		GpuProgramParametersSharedPtr param1 = gridMat->getTechnique(0)->getPass(0)->getVertexProgramParameters();
		Vector3 vGridDim((Real)gridDim, gridDim*0.5f, 2.0f/gridDim);
		param1->setNamedConstant("g_gridDim", vGridDim);

		Entity* ent = mSceneMgr->createEntity("TerrainGridEntity1", mMesh->getMesh(9)->getName());
		ent->setMaterialName("TerrainGridMaterialRed");
		SceneNode * node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		node->attachObject(ent);
		node->scale(640, 1, 640);
		node->setPosition(0, 0, 0);
		
		ent = mSceneMgr->createEntity("TerrainGridEntity0", mMesh->getMesh(15)->getName());
		ent->setMaterialName("TerrainGridMaterial");
		node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		node->attachObject(ent);
		node->scale(640, 1, 640);
		node->setPosition(0, 0, -640);
*/		
		mCamera->setPosition(0, 500, 500);
		mCamera->lookAt(0, 0, 0);

		g_QuadTree.setVisibleRanges(MAX_NODE_LEVEL, lodRanges);

		mSceneMgr->addListener(&g_QTListener);
	}
};


#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
    // Create application object
    TestApplication app(strCmdLine);

    try {
        app.go();
    } catch( Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "An exception has occured: " << e.getFullDescription();
#endif
    }


    return 0;
}

#ifdef __cplusplus
}
#endif